/*!
 * 无限级下拉列表级联插件
 * SelectCascade JavaScript Library v1.0.0
 * https://github.com/chengxg/my-js-plug/tree/master/SelectCascade
 * https://gitee.com/chengxiaogang/my-js-plug/tree/master/SelectCascade
 *
 * Copyright chengxg
 * Released under the MIT license
 *
 * Date: 2017-11-06
 */

(function(window, factory, plug) {
	factory(window, plug);
})(this, function(window, plug) {
	/**
	 * 默认操作参数
	 */
	var defaultOptions = {
		//第一级列表的parentId
		ROOT: "root",
		//列表开始渲染完成时的回调
		renderStart: function() {

		},
		//列表全部渲染完成时的回调
		renderEnd: function() {

		}
	};

	//添加数组IndexOf方法,ie8支持
	if(!Array.prototype.indexOf) {
		Array.prototype.indexOf = function(elt /*, from*/ ) {
			var len = this.length >>> 0;

			var from = Number(arguments[1]) || 0;
			from = (from < 0) ?
				Math.ceil(from) :
				Math.floor(from);
			if(from < 0)
				from += len;

			for(; from < len; from++) {
				if(from in this && this[from] === elt)
					return from;
			}
			return -1;
		};
	}

	/**
	 * 判断类型是否为Array
	 * @param {Object} o
	 * @return {boolean}
	 */
	function _isArray(o) {
		return Object.prototype.toString.call(o) == '[object Array]';
	}

	/**
	 * 合并对象字段,后边对象的字段覆盖前面的
	 * @return {Object} 合并后的对象
	 */
	function _mergeObject(target) {
		var i = 0,
			sourceLen = arguments.length;
		var filed = "";

		for(i = 1; i < sourceLen; i++) {
			var source = arguments[i];
			if(!source) {
				continue;
			}
			for(filed in source) {
				target[filed] = source[filed];
			}
		}
		return target;
	}

	/**
	 * 添加数据到缓存
	 * @param {Object} selectDataArr 下拉列表数据
	 * @param {Object} selectLevel 列表的层级
	 */
	function _pushDataToCache(selectDataArr, selectLevel) {
		var cache = this.cache;
		if(selectDataArr) {
			var len = selectDataArr.length;
			var item = null;
			for(var i = 0; i < len; i++) {
				item = selectDataArr[i];
				if(!item) {
					continue;
				}
				item.selectLevel = selectLevel;
				_checkDataFiled(item);
				cache.push(item);
			}
		}
	}

	/**
	 * 检查列表数据格式,必须存在字段[id],[parentId]
	 * @param {Object} data
	 */
	function _checkDataFiled(data) {
		if(data) {
			if(typeof data["id"] === 'undefined') {
				throw new Error("列表数据字段[id]不存在");
			}
			if(typeof data["parentId"] === 'undefined') {
				throw new Error("列表数据字段[parentId]不存在");
			}
		}
	}

	/**
	 * 从缓存中获取列表数据
	 * @param {Object} id 列表id
	 * @return {Object} dataObj 列表数据
	 */
	function _obtainDataFromCacheById(id) {
		var cache = this.cache;
		var i = 0,
			len = cache.length;
		for(i = 0; i < len; i++) {
			var dataObj = cache[i];
			if(dataObj["id"] === id) {
				return dataObj
			}
		}
		return null;
	}

	/**
	 * 从缓存中获取 子列表数据
	 * @param {Object} parentId
	 * @return {Array} 列表子类
	 */
	function _searchDataChildrenFromCacheByParentId(parentId) {
		var cache = this.cache;
		var i = 0,
			len = cache.length;
		var children = [];
		if(typeof parentId === 'undefined') {
			return _searchFirstData.call(this);
		}
		for(i = 0; i < len; i++) {
			var dataObj = cache[i];
			if(dataObj["parentId"] == parentId) {
				children.push(dataObj);
			}
		}
		return children;
	}

	/**
	 * 从缓存中获取第一级列表数据
	 * @return {Array} 列表子类
	 */
	function _searchFirstData() {
		var cache = this.cache;
		var i = 0,
			len = cache.length;
		var children = [];

		for(i = 0; i < len; i++) {
			var dataObj = cache[i];
			if(dataObj["selectLevel"] === 0) {
				children.push(dataObj);
			}
		}
		return children;
	}

	/**
	 * 判断该列表id 是否存在于 指定数组中
	 * @param {Object} selectDataArr
	 * @param {Object} selectId
	 */
	function _isContainSelectId(selectDataArr, selectId) {
		if(selectId === null) {
			return false;
		}
		if(!selectDataArr || selectDataArr.length === 0) {
			return false;
		}
		var len = selectDataArr.length;
		var item = null;
		for(var i = 0; i < len; i++) {
			item = selectDataArr[i];
			if(item && item.id === selectId) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 设置列表配置
	 * @param {Array} selectConfigArr
	 */
	function _setAndCheckSelectConfigArr(selectConfigArr) {
		var selectCascade = this;

		if(selectConfigArr && _isArray(selectConfigArr) && selectConfigArr.length > 0) {
			var i = 0,
				len = selectConfigArr.length,
				item = null;
			var filed = "";
			for(i = 0; i < len; i++) {
				item = selectConfigArr[i];
				if(item && typeof item === 'object') {
					var getSelectData = item["getSelectData"];
					if(!(getSelectData && typeof getSelectData === 'function')) {
						throw new Error("第" + (i + 1) + "个列表配置参数getSelectData,类型不是函数");
					}
					var renderSelect = item["renderSelect"];
					if(!(renderSelect && typeof renderSelect === 'function')) {
						throw new Error("第" + (i + 1) + "个列表配置参数renderSelect,类型不是函数");
					}
				} else {
					throw new Error("第" + (i + 1) + "个列表配置参数错误!");
				}
			}
			selectCascade.selectConfigArr = selectConfigArr;
		} else {
			throw new Error("级联列表配置错误!");
		}
	}

	/**
	 * 设置操作参数
	 * @param {Object} options
	 */
	function _setOptions(options) {
		var selectCascade = this;
		var defFiled = null;
		var newOptions = _mergeObject({}, defaultOptions, options);
		selectCascade.options = newOptions;
	}

	/**
	 * 列表级联构造函数
	 * 
	 * @constructor
	 * @param {Array} selectConfigArr 关联的列表配置参数
	 * @param {Object} options 操作参数
	 */
	function SelectCascade(selectConfigArr, options) {
		this.selectConfigArr = null;
		this.options = {};
		_setAndCheckSelectConfigArr.call(this, selectConfigArr);
		_setOptions.call(this, options);
		this.haveQueryParentIdArr = []; //保存已经从服务器查询过的父级id

		/**
		 * 列表数据缓存,是一个一维数组,第一项为第一级列表的父级对象
		 * 字段[id]唯一标识列表数据
		 * 通过[parentId]来获取子列表数据
		 * 字段[selectLevel],记录该列表数据所属的层级
		 */
		this.cache = [{
			id: this.options.ROOT,
			parentId: "",
			val: "",
			selectLevel: -1
		}];
	}

	/**
	 * 对外公开方法
	 * 加载级联的列表
	 * 
	 * @param {Object} currentDefaultOptions 本次列表加载的配置
	 * @param {Array} currentDefaultOptions.defaultSelectIdArr 本次列表加载的父级id链,如果为空则从第一级列表开始渲染
	 * @param {Function} currentDefaultOptions.renderStart 本次列表加载开始时的回调
	 * @param {Function} currentDefaultOptions.renderEnd 本次列表加载完成时的回调
	 */
	SelectCascade.prototype.renderSelectCascade = function(currentDefaultOptions) {
		currentDefaultOptions = currentDefaultOptions || {};
		var defaultSelectIdArr = currentDefaultOptions.defaultSelectIdArr;
		var renderEnd = currentDefaultOptions.renderEnd || function() {};
		var renderStart = currentDefaultOptions.renderStart || function() {};
		var startLevel = currentDefaultOptions.startLevel;

		var selectCascade = this; //当前对象的引用
		var selectConfigArr = this.selectConfigArr; //列表配置
		var selectLevelCount = selectConfigArr.length; //下拉列表级联的层级总数

		var parentId = null; //父级id
		if(defaultSelectIdArr) {
			if(!_isArray(defaultSelectIdArr)) {
				throw new Error("传入的参数类型不是数组!");
			}
			if(defaultSelectIdArr.length > 0) {
				parentId = defaultSelectIdArr.shift();
				if(parentId === null) {
					throw new Error("父级id不能为空!");
				}
			} else {
				throw new Error("参数defaultSelectIdArr不能为空!");
			}
		} else {
			throw new Error("未指定参数defaultSelectIdArr!");
		}

		//当前层级计数,最顶层为-1
		var currentLevel = null;
		//获取到父级数据
		var parentSelectObj = _obtainDataFromCacheById.call(selectCascade, parentId);
		if(parentSelectObj) {
			//设置当前开始渲染的列表层级
			currentLevel = parentSelectObj.selectLevel;
		}
		if(typeof startLevel !== 'undefined') {
			currentLevel = startLevel * 1;
		}

		//从下一级开始初始化列表
		initSelect(currentLevel + 1);
		renderStartFuncs();
		next();

		/**
		 * 渲染开始
		 */
		function renderStartFuncs() {
			renderStart(); //本次结束时的回调
			selectCascade.options.renderStart(); //全部渲染完成时的回调
		}

		/**
		 * 渲染下一层级列表(由用户控制)
		 * @param {String} setParentId 指定下一级的父级id
		 */
		function next(setParentId) {
			//指定下一级的父级id
			if(typeof setParentId !== 'undefined') {
				parentId = setParentId;
			}

			currentLevel++;
			if(currentLevel < selectLevelCount) {
				renderSelect();
			} else {
				renderEndFuncs();
			}
		}

		/**
		 * 渲染结束
		 */
		function renderEndFuncs() {
			renderEnd(); //本次结束时的回调
			selectCascade.options.renderEnd(); //全部渲染完成时的回调
		}

		/**
		 * 渲染单个列表
		 */
		function renderSelect() {
			if(!parentId) {
				renderEndFuncs();
				return;
			}

			var childrenDataArr = _searchDataChildrenFromCacheByParentId.call(selectCascade, parentId);

			var conf = selectConfigArr[currentLevel]; //当前层级的配置对象
			if(childrenDataArr.length <= 0) {
				//该parentId未查询过则从服务器取数据
				if(selectCascade.haveQueryParentIdArr.indexOf(parentId) === -1) {
					//如果未从缓存中获取到子类,则从服务来获取
					var getSelectData = conf.getSelectData;
					getSelectData(parentId, function(data) {
						_pushDataToCache.call(selectCascade, data, currentLevel);
						selectCascade.haveQueryParentIdArr.push(parentId); //保存以经查询过的parentId
						render(data);
					});
				} else {
					render(childrenDataArr);
				}
			} else {
				render(childrenDataArr);
			}

			function render(selectDataArr) {
				if(selectDataArr.length === 0) {
					renderEndFuncs();
					return;
				}
				var selectId = null;

				//得到该列表默认的选择项
				if(defaultSelectIdArr && defaultSelectIdArr.length > 0) {
					selectId = defaultSelectIdArr.shift();
				}
				//没有指定则默认选择第一个
				if(selectId === null) {
					selectId = selectDataArr[0].id;
				}

				parentId = selectId; //设置下一级列表的父级id

				//用户自定义渲染函数
				conf.renderSelect(selectDataArr, selectId, next);
			}
		}

		/**
		 * 从startIndex级列表 初始化列表
		 * @param {Number} startIndex 列表层级
		 */
		function initSelect(startIndex) {
			for(var i = startIndex; i < selectLevelCount; i++) {
				var conf = selectConfigArr[i];
				conf.renderSelect([], null, null);
			}
		}
	}

	/**
	 * 对外公开方法
	 * 添加列表数据到缓存,列表数据需提供[selectLevel]字段
	 * @param {Array} dataArr 数据格式[{
	 *	id: "0012",
	 *	parentId: "001",
	 *	val: "001",
	 *	selectLevel: 0
	 * }]
	 */
	SelectCascade.prototype.addSelectDataToCache = function(dataArr) {
		var cache = this.cache;
		if(_isArray(dataArr)) {
			var i, len = dataArr.length,
				item;
			for(i = 0; i < len; i++) {
				item = dataArr[i];
				if(!item) {
					continue;
				}
				_checkDataFiled(item);
				if(typeof item["selectLevel"] !== 'undefined') {
					cache.push(item);
				} else {
					throw new Error("列表数据未获取到[selectLevel]属性!");
				}
			}
		}
		return this;
	}

	/**
	 * 通过parentId刷新直接子级列表
	 * @param {String} parentId
	 * @param {Array} newDataArr
	 */
	SelectCascade.prototype.refreshCacheByParentId = function(parentId, newDataArr) {
		var cache = this.cache;
		var i = 0,
			len = cache.length;

		//删除缓存中parentId的直接子级
		var newCache = [];
		for(i = 0; i < len; i++) {
			var dataObj = cache[i];
			if(dataObj["parentId"] != parentId) {
				newCache.push(dataObj);
			}
		}
		this.cache = newCache;

		if(typeof newDataArr !== 'undefined') {
			this.addSelectDataToCache();
		} else {
			//删除查询缓存
			var historyParentIdIndex = this.haveQueryParentIdArr.indexOf(parentId);
			if(historyParentIdIndex !== -1) {
				this.haveQueryParentIdArr.splice(historyParentIdIndex, 1);
			}
		}
		return this;
	}

	/**
	 * 通过id从缓存中查询列表数据
	 * @param {String} id
	 * @return {Object} data
	 */
	SelectCascade.prototype.obtainCacheDataById = function(id) {
		return _obtainDataFromCacheById.call(this, id);
	}

	/**
	 * 通过parentId从缓存中查询列表数据
	 * @param {String} parentId
	 * @return {Array} dataArray
	 */
	SelectCascade.prototype.findCacheDataByParentId = function(parentId) {
		return _searchDataChildrenFromCacheByParentId.call(this, parentId);
	}

	/**
	 * 通过id从缓存中查询同级列表数据
	 * @param {String} id
	 * @return {Array} dataArray
	 */
	SelectCascade.prototype.findCurrentSelectDataById = function(id) {
		var dataObj = _obtainDataFromCacheById.call(this, id);
		if(dataObj) {
			return _searchDataChildrenFromCacheByParentId.call(this, dataObj["parentId"]);
		}
		return [];
	}

	//将插件构造函数添加到全局对象
	window[plug] = SelectCascade;

}, "SelectCascade");